/* ----------
 * backend_status.h
 *	  与后端状态报告相关的定义
 *
 * Copyright (c) 2001-2022, PostgreSQL Global Development Group
 *
 * src/include/utils/backend_status.h
 * ----------
 */
#ifndef BACKEND_STATUS_H
#define BACKEND_STATUS_H

#include "datatype/timestamp.h"
#include "libpq/pqcomm.h"
#include "miscadmin.h"			/* 针对 BackendType */
#include "utils/backend_progress.h"


/* ----------
 * 后端状态
 * ----------
 */
typedef enum BackendState
{
	STATE_UNDEFINED,
	STATE_IDLE,
	STATE_RUNNING,
	STATE_IDLEINTRANSACTION,
	STATE_FASTPATH,
	STATE_IDLEINTRANSACTION_ABORTED,
	STATE_DISABLED
} BackendState;


/* ----------
 * 共享内存数据结构
 * ----------
 */

/*
 * PgBackendSSLStatus
 *
 * 对于每个后端，我们在一个单独的结构中保留 SSL 状态，
 * 只有当 SSL 被启用时，才会填写。
 *
 * 所有字符数组必须以 null 结尾。
 */
typedef struct PgBackendSSLStatus
{
	/* 关于 SSL 连接的信息 */
	int			ssl_bits;
	char		ssl_version[NAMEDATALEN];
	char		ssl_cipher[NAMEDATALEN];
	char		ssl_client_dn[NAMEDATALEN];

	/*
	 * 序列号最大为 "20 字节"，根据 RFC 5280，因此这个大小应该是
	 * 可以接受的
	 */
	char		ssl_client_serial[NAMEDATALEN];

	char		ssl_issuer_dn[NAMEDATALEN];
} PgBackendSSLStatus;

/*
 * PgBackendGSSStatus
 *
 * 对于每个后端，我们在一个单独的结构中保留 GSS 状态，
 * 只有当 GSS 被启用时，才会填写。
 *
 * 所有字符数组必须以 null 结尾。
 */
typedef struct PgBackendGSSStatus
{
	/* 关于 GSSAPI 连接的信息 */
	char		gss_princ[NAMEDATALEN]; /* 用于认证的 GSSAPI 主体 */
	bool		gss_auth;		/* 如果使用了 GSSAPI 认证 */
	bool		gss_enc;		/* 如果正在使用加密 */

} PgBackendGSSStatus;


/* ----------
 * PgBackendStatus
 *
 * 每个活跃的后端在共享内存中维护一个 PgBackendStatus 结构
 * 显示其当前活动。 （这些结构根据 BackendId 分配，
 * 但这并不关键。） 注意，这与累积统计系统无关
 * （即 pgstat.c 等）。
 *
 * 每个辅助进程也在共享内存中维护一个 PgBackendStatus 结构。
 * ----------
 */
typedef struct PgBackendStatus
{
	/*
	 * 为了避免锁定开销，我们使用以下协议：一个后端
	 * 在修改其条目之前和完成修改后都会递增 st_changecount。
	 * 试图读取的过程应该注意 st_changecount 的值，将条目
	 * 复制到私有内存中，然后再次检查 st_changecount。
	 * 如果值没有改变，并且是偶数，复制是有效的；
	 * 否则重新开始。这使得更新成本低
	 * 而读取可能成本较高，但这是我们所希望的权衡。
	 *
	 * 上述协议需要内存屏障，以确保执行的表面顺序如预期那样。
	 * 否则，例如，CPU 可能会重新排列代码，导致在
	 * 内存顺序较弱的机器上，st_changecount 在修改之前被递增两次。
	 * 因此，请使用下面定义的宏来操作 st_changecount，
	 * 而不是直接接触它。
	 */
	int			st_changecount;

	/* 条目是有效的，仅当 st_procpid > 0 时，有效性无效如果 st_procpid == 0 */
	int			st_procpid;

	/* 后端的类型 */
	BackendType st_backendType;

	/* 当前后端、事务和活动开始的时间 */
	TimestampTz st_proc_start_timestamp;
	TimestampTz st_xact_start_timestamp;
	TimestampTz st_activity_start_timestamp;
	TimestampTz st_state_start_timestamp;

	/* 数据库 OID、拥有用户的 OID、连接客户端地址 */
	Oid			st_databaseid;
	Oid			st_userid;
	SockAddr	st_clientaddr;
	char	   *st_clienthostname;	/* 必须以 null 结尾 */

	/* 关于 SSL 连接的信息 */
	bool		st_ssl;
	PgBackendSSLStatus *st_sslstatus;

	/* 关于 GSSAPI 连接的信息 */
	bool		st_gss;
	PgBackendGSSStatus *st_gssstatus;

	/* 当前状态 */
	BackendState st_state;

	/* 应用程序名称；必须以 null 结尾 */
	char	   *st_appname;

	/*
	 * 当前命令字符串；必须以 null 结尾。 注意，这个字符串
	 * 可能在多字节字符中间被截断。由于
	 * 活动字符串存储的频率高于读取，这允许我们
	 * 将正确截断的成本转移到显示端。请使用
	 * pgstat_clip_activity() 来正确截断。
	 */
	char	   *st_activity_raw;

	/*
	 * 命令进度报告。任何希望报告正在运行的命令
	 * 可以通过设置 st_progress_command、
	 * st_progress_command_target 和 st_progress_param[] 来宣传
	 * st_progress_command_target 应该是命令所针对的关系的 OID
	 * （我们假设只有一个，因为这是为了效用命令），
	 * 但是 st_progress_param 数组中每个元素的含义是特定于命令的。
	 */
	ProgressCommandType st_progress_command;
	Oid			st_progress_command_target;
	int64		st_progress_param[PGSTAT_NUM_PROGRESS_PARAM];

	/* 查询标识符，可选地使用 post_parse_analyze_hook 计算 */
	uint64		st_query_id;
} PgBackendStatus;


/*
 * 用于加载和存储 st_changecount 的宏，具备适当的内存屏障。
 *
 * 在修改当前进程的 PgBackendStatus 数据之前使用 PGSTAT_BEGIN_WRITE_ACTIVITY()，
 * 在修改之后使用 PGSTAT_END_WRITE_ACTIVITY()。注意，
 * 由于没有清理 st_changecount 的机制，
 * 这些宏构成了临界区。它们之间的任何错误都将
 * 提升为 PANIC，导致数据库重启以清理共享内存！
 * 因此，请将临界区保持尽可能短且直线。
 * 除了安全性，这样还最小化了读取者需要循环的窗口。
 *
 * 读取逻辑应遵循以下草图：
 *
 *		for (;;)
 *		{
 *			int before_ct, after_ct;
 *
 *			pgstat_begin_read_activity(beentry, before_ct);
 *			... 将 beentry 数据复制到本地内存 ...
 *			pgstat_end_read_activity(beentry, after_ct);
 *			if (pgstat_read_activity_complete(before_ct, after_ct))
 *				break;
 *			CHECK_FOR_INTERRUPTS();
 *		}
 *
 * 为了额外的安全性，我们通常使用 volatile beentry 指针，尽管
 * 内存屏障理论上应该足够。
 */
#define PGSTAT_BEGIN_WRITE_ACTIVITY(beentry) \
	do { \
		START_CRIT_SECTION(); \
		(beentry)->st_changecount++; \
		pg_write_barrier(); \
	} while (0)

#define PGSTAT_END_WRITE_ACTIVITY(beentry) \
	do { \
		pg_write_barrier(); \
		(beentry)->st_changecount++; \
		Assert(((beentry)->st_changecount & 1) == 0); \
		END_CRIT_SECTION(); \
	} while (0)

#define pgstat_begin_read_activity(beentry, before_changecount) \
	do { \
		(before_changecount) = (beentry)->st_changecount; \
		pg_read_barrier(); \
	} while (0)

#define pgstat_end_read_activity(beentry, after_changecount) \
	do { \
		pg_read_barrier(); \
		(after_changecount) = (beentry)->st_changecount; \
	} while (0)

#define pgstat_read_activity_complete(before_changecount, after_changecount) \
	((before_changecount) == (after_changecount) && \
	 ((before_changecount) & 1) == 0)



/* ----------
 * LocalPgBackendStatus
 *
 * 当我们构建后端状态数组时，我们使用LocalPgBackendStatus以便在需要时能向结构中添加新值，而无需向共享内存中添加新字段。它将后端状态作为第一个成员。
 * ----------
 */
typedef struct LocalPgBackendStatus
{
	/*
	 * 后端状态项的本地版本。
	 */
	PgBackendStatus backendStatus;

	/*
	 * 当前事务的xid，如果可用；如果不可用，则为InvalidTransactionId。
	 */
	TransactionId backend_xid;

	/*
	 * 当前会话的xmin，如果可用的话；如果不可用则为InvalidTransactionId。
	 */
	TransactionId backend_xmin;
} LocalPgBackendStatus;


/* ----------
 * GUC 参数
 * ----------
 */
extern PGDLLIMPORT bool pgstat_track_activities;
extern PGDLLIMPORT int pgstat_track_activity_query_size;


/* ----------
 * 其他全局变量
 * ----------
 */
extern PGDLLIMPORT PgBackendStatus *MyBEEntry;


/* ----------
 * 从postmaster调用的函数
 * ----------
 */
extern Size BackendStatusShmemSize(void);
extern void CreateSharedBackendStatus(void);


/* ----------
 * 从后端调用的函数
 * ----------
 */

/* 初始化函数 */
extern void pgstat_beinit(void);
extern void pgstat_bestart(void);

extern void pgstat_clear_backend_activity_snapshot(void);

/* 活动报告函数 */
extern void pgstat_report_activity(BackendState state, const char *cmd_str);
extern void pgstat_report_query_id(uint64 query_id, bool force);
extern void pgstat_report_tempfile(size_t filesize);
extern void pgstat_report_appname(const char *appname);
extern void pgstat_report_xact_timestamp(TimestampTz tstamp);
extern const char *pgstat_get_backend_current_activity(int pid, bool checkUser);
extern const char *pgstat_get_crashed_backend_activity(int pid, char *buffer,
													   int buflen);
extern uint64 pgstat_get_my_query_id(void);


/* ----------
 * 支持SQL可调用函数生成pgstat*视图的函数。
 * ----------
 */
extern int	pgstat_fetch_stat_numbackends(void);
extern PgBackendStatus *pgstat_fetch_stat_beentry(int beid);
extern LocalPgBackendStatus *pgstat_fetch_stat_local_beentry(int beid);
extern char *pgstat_clip_activity(const char *raw_activity);


#endif							/* BACKEND_STATUS_H */
